﻿' Copyright © Microsoft Corporation.  All Rights Reserved.
' This code released under the terms of the 
' Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)
' <copyright file="PlaylistItem.vb" company="Microsoft">
'     Copyright © Microsoft Corporation. All rights reserved.
' </copyright>
' <summary>Implements the PlaylistItem class</summary>
' <author>Microsoft Expression Encoder Team</author>

Imports Microsoft.VisualBasic
Imports System
Imports System.ComponentModel
Imports System.Windows.Browser
Imports System.Xml
Imports System.Globalization

Namespace ExpressionMediaPlayer
	''' <summary>
	''' This class represents a media item in a playlist.
	''' </summary>
	<ScriptableType> _
	Public Class PlaylistItem
		Implements INotifyPropertyChanged, IAccessible
		''' <summary>
		''' The parent collection of this item.
		''' </summary>
		Private m_collectionParent As PlaylistCollection
		''' <summary>
		''' The title of this item.
		''' </summary>
		Private m_title As String
		''' <summary>
		''' The description of this item.
		''' </summary>
		Private m_description As String
		''' <summary>
		''' The thumbnail source for this item.
		''' </summary>
		Private m_thumbSource As Uri
		''' <summary>
		''' The width of the encoded video for this item.
		''' </summary>
		Private m_width As Double
		''' <summary>
		''' The height of the encoded video for this item.
		''' </summary>
		Private m_height As Double
		''' <summary>
		''' total filesize of this item
		''' </summary>
		Private m_fileSize As Long
		''' <summary>
		''' The Url for the media of this item.
		''' </summary>
		Private m_mediaUri As Uri
		''' <summary>
		''' A value indicating whether this item is adaptive streaming or not.
		''' </summary>
		Private m_isAdaptiveStreaming As Boolean
		''' <summary>
		''' The video bitrate of the adaptive stream that was downloaded and stored in isolated storage for offline playback. 
		''' </summary>
		Private m_offlineVideoBitrateInKbps As Long
		''' <summary>
		''' The frame rate of this item.
		''' </summary>
		Private m_frameRate As SmpteFrameRate = SmpteFrameRate.Unknown
		''' <summary>
		''' frame rate in FPS as persisted.
		''' </summary>
		Private m_frameRateFPS As Double
		''' <summary>
		''' The chapters in this item.
		''' </summary>
		Private m_chapters As New ScriptableObservableCollection(Of ChapterItem)()
		''' <summary>
		''' Property changed event.
		''' </summary>
		Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
		''' <summary>
		''' init structure
		''' </summary>
		Private Sub Init()
			m_title = String.Empty
			m_description = String.Empty
			m_thumbSource = Nothing
			m_fileSize = 0
			m_frameRate = SmpteFrameRate.Smpte30
			m_width = 640
			m_height = 480
			m_offlineVideoBitrateInKbps = 0
		End Sub
		''' <summary>
		''' parameterless constructor required for Edit in Blend.
		''' </summary>
		Public Sub New()
			m_chapters = New ScriptableObservableCollection(Of ChapterItem)()
			AddHandler m_chapters.CollectionChanged, AddressOf Chapters_CollectionChanged
			Init()
		End Sub
		''' <summary>
		''' Initializes a new instance of the PlaylistItem class.
		''' </summary>
		Public Sub New(ByVal collectionParent As PlaylistCollection)
			Init()
			m_collectionParent = collectionParent
		End Sub
		''' <summary>
		''' Sets the owner collection. Required to enable declaritve collections 
		''' where playlistitems are instantiated in XAML with default constructor.
		''' </summary>
		Friend WriteOnly Property OwnerCollection() As PlaylistCollection
			Set(ByVal value As PlaylistCollection)
				m_collectionParent = value
			End Set
		End Property
		''' <summary>
		''' constructor provided for scripting.
		''' </summary>
		''' <returns>new chapter</returns>
		Public Function CreateNewChapterItem() As ChapterItem
			Return New ChapterItem()
		End Function
		''' <summary>
		''' Gets the index of this item in the collection.
		''' </summary>
		<System.ComponentModel.EditorBrowsable(EditorBrowsableState.Never)> _
		Public ReadOnly Property PlaylistIndex() As Integer
			Get
				If m_collectionParent IsNot Nothing Then
					Return m_collectionParent.IndexOf(Me) + 1
				Else
					Return -1
				End If
			End Get
		End Property
		''' <summary>
		''' Gets or sets the description of this playlist item.
		''' </summary>
		<Description("Description of media item")> _
		Public Property Description() As String
			Get
				Return m_description
			End Get

			Set(ByVal value As String)
				m_description = value
				OnPropertyChanged("Description")
			End Set
		End Property
		''' <summary>
		''' Gets the total file size of the encoded video for this item.
		''' </summary>
		<Description("file size of item in bytes"), DefaultValue(0)> _
		Public Property FileSize() As Long
			Get
				Return m_fileSize
			End Get
			Set(ByVal value As Long)
				m_fileSize = value
			End Set
		End Property
		''' <summary>
		''' frame rate in FPS as persisted.
		''' </summary>
		<Description("frame rate in frames per second"), DefaultValue(30)> _
		Public Property FrameRate() As Double
			Get
				Return m_frameRateFPS
			End Get
			Set(ByVal value As Double)
				m_frameRateFPS = value
				SmpteFrameRate = TimeCode.ParseFrameRate(value)
			End Set
		End Property
		''' <summary>
		''' Gets the width of the encoded video for this item.
		''' </summary>
		<Description("height in pixels"), DefaultValue(480)> _
		Public Property VideoHeight() As Double
			Get
				Return m_height
			End Get
			Set(ByVal value As Double)
				m_height = value
			End Set
		End Property
		''' <summary>
		''' Gets or sets a value indicating whether this item uses adaptive streaming.
		''' </summary>
		<Description("is adaptive streaming item")> _
		Public Property IsAdaptiveStreaming() As Boolean
			Get
				Return m_isAdaptiveStreaming
			End Get

			Set(ByVal value As Boolean)
				m_isAdaptiveStreaming = value
				OnPropertyChanged("IsAdaptiveStreaming")
			End Set
		End Property
		''' <summary>
		''' Gets the offline video bitrate used for adaptive streaming.
		''' Note this item is not scriptable
		''' </summary>
		Friend Property OfflineVideoBitrateInKbps() As Long
			Get
				Return m_offlineVideoBitrateInKbps
			End Get
			Set(ByVal value As Long)
				m_offlineVideoBitrateInKbps = value
			End Set
		End Property
		''' <summary>
		''' Gets or sets the Url of the media item.
		''' </summary>
		<Description("uri of item")> _
		Public Property MediaSource() As Uri
			Get
				Return m_mediaUri
			End Get

			Set(ByVal value As Uri)
				m_mediaUri = value
				OnPropertyChanged("MediaSource")
			End Set
		End Property
		''' <summary>
		''' Gets or sets the source of the thumbnail for this item. 
		''' Uses a string instead of a URI to facilitate binding and handling cases where 
		''' the thumbnail file is missing without generating a page error.
		''' </summary>
		<Description("optional thumbnail for gallery and poster frame")> _
		Public Property ThumbSource() As Uri
			Get
				Return m_thumbSource
			End Get

			Set(ByVal value As Uri)
				m_thumbSource = value
				OnPropertyChanged("ThumbSource")
			End Set
		End Property
		''' <summary>
		''' Gets or sets the title of the playlist item.
		''' </summary>
		<Description("title of item")> _
		Public Property Title() As String
			Get
				Return m_title
			End Get

			Set(ByVal value As String)
				m_title = value
				OnPropertyChanged("Title")
			End Set
		End Property
		''' <summary>
		''' Gets the width of the encoded video for this item.
		''' </summary>
		<Description("width in pixels"), DefaultValue(640)> _
		Public Property VideoWidth() As Double
			Get
				Return m_width
			End Get
			Set(ByVal value As Double)
				m_width = value
			End Set
		End Property
		''' <summary>
		''' Gets the chapters in this item.
		''' </summary>
		<Description("list of chapters")> _
		Public Property Chapters() As ScriptableObservableCollection(Of ChapterItem)
			Get
				Return m_chapters
			End Get
			Set(ByVal value As ScriptableObservableCollection(Of ChapterItem))
				m_chapters = value
			End Set
		End Property
		''' <summary>
		''' Gets or sets the frame rate of this item.
		''' </summary>
		<EditorBrowsable(EditorBrowsableState.Never)> _
		Public Property SmpteFrameRate() As SmpteFrameRate
			Get
				Return m_frameRate
			End Get
			Set(ByVal value As SmpteFrameRate)
				m_frameRate = value
				m_frameRateFPS = 1 / TimeCode.FromFrames(1, value).TotalSeconds
				OnPropertyChanged("FrameRate")
			End Set
		End Property
		''' <summary>
		''' chapters collection changed. means this playlist item changed
		''' </summary>
		''' <param name="sender"></param>
		''' <param name="e"></param>
		Private Sub Chapters_CollectionChanged(ByVal sender As Object, ByVal e As System.Collections.Specialized.NotifyCollectionChangedEventArgs)
			OnPropertyChanged("Chapters")
		End Sub
		''' <summary>
		''' accessibility text, will be read out by screen reader if your listbox item has a control 
		''' AutomationProperties.Name bound to this
		''' </summary>
		Private ReadOnly Property AccessibilityText() As String Implements IAccessible.AccessibilityText
			Get
				Return String.Format(CultureInfo.InvariantCulture, ExpressionMediaPlayer.Resources.readerPlaylistItem, Title, Description)
			End Get
		End Property
		#Region "INotifyPropertyChanged Members"
		''' <summary>
		''' Implements INotifyPropertyChanged.OnPropertyChanged().
		''' </summary>
		''' <param name="memberName">The name of the property that changed.</param>
		Protected Sub OnPropertyChanged(ByVal memberName As String)
			RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(memberName))
		End Sub
		#End Region
		#Region "Serialization"
		''' <summary>
		''' top level XML node for this class & chapters
		''' </summary>
		Friend Const xmlNode As String = "PlaylistItem"
		Friend Const xmlChaptersNode As String = "Chapters"
		''' <summary>
		''' deserialise chapters
		''' </summary>
		''' <param name="reader">XmlReader to deserialize from</param>
		''' <returns>this</returns>
		Private Sub DeserializeChapters(ByVal reader As XmlReader)
			If Not reader.IsStartElement(PlaylistItem.xmlChaptersNode) Then
				Throw New InvalidPlaylistException()
			End If

			reader.Read()
			Do While Not(reader.Name = PlaylistItem.xmlChaptersNode AndAlso reader.NodeType = XmlNodeType.EndElement)
				If reader.IsStartElement("ChapterItem") Then
					Me.Chapters.Add(New ChapterItem().Deserialize(reader))
				ElseIf reader.IsStartElement() Then
					Throw New InvalidPlaylistException(reader.Name, PlaylistItem.xmlChaptersNode)
				ElseIf Not reader.Read() Then
					Exit Do
				End If
			Loop
		End Sub
		''' <summary>
		''' deserialise this object
		''' </summary>
		''' <param name="reader">XmlReader to deserialize from</param>
		''' <returns>this</returns>
		Friend Function Deserialize(ByVal reader As XmlReader) As PlaylistItem
			If Not reader.IsStartElement(PlaylistItem.xmlNode) Then
				Throw New InvalidPlaylistException()
			End If

			Init()
			reader.Read()
			Do While Not(reader.Name = PlaylistItem.xmlNode AndAlso reader.NodeType = XmlNodeType.EndElement)
				If reader.IsStartElement("Description") Then
					Me.Description = reader.ReadElementContentAsString()
				ElseIf reader.IsStartElement("FileSize") Then
					Me.FileSize = reader.ReadElementContentAsLong()
				ElseIf reader.IsStartElement("FrameRate") Then
					Me.FrameRate = reader.ReadElementContentAsDouble()
				ElseIf reader.IsStartElement("Height") Then
					Me.VideoHeight = reader.ReadElementContentAsDouble()
				ElseIf reader.IsStartElement("IsAdaptiveStreaming") Then
					Me.IsAdaptiveStreaming = reader.ReadElementContentAsBoolean()
				ElseIf reader.IsStartElement("OfflineVideoBitrateInKbps") Then
					Me.OfflineVideoBitrateInKbps = reader.ReadElementContentAsLong()
				ElseIf reader.IsStartElement("MediaSource") Then
					Dim rawMediaSourceUrl As String = reader.ReadElementContentAsString()
					Dim decodedMediaSourceUrl As String = HttpUtility.UrlDecode(rawMediaSourceUrl)
					Me.MediaSource = New Uri(decodedMediaSourceUrl, UriKind.RelativeOrAbsolute)
				ElseIf reader.IsStartElement("ThumbSource") Then
					Dim rawThumbSourceUrl As String = reader.ReadElementContentAsString()
					Dim decodedThumbSourceUrl As String = HttpUtility.UrlDecode(rawThumbSourceUrl)
					Me.ThumbSource = New Uri(decodedThumbSourceUrl, UriKind.RelativeOrAbsolute)
				ElseIf reader.IsStartElement("Title") Then
					Me.Title = reader.ReadElementContentAsString()
				ElseIf reader.IsStartElement("Width") Then
					Me.VideoWidth = reader.ReadElementContentAsDouble()
				ElseIf reader.IsStartElement(PlaylistItem.xmlChaptersNode) Then
					DeserializeChapters(reader)
				ElseIf reader.IsStartElement("AudioCodec") Then
					reader.ReadElementContentAsObject() ' ignored
				ElseIf reader.IsStartElement("VideoCodec") Then
					reader.ReadElementContentAsObject() ' ignored
				ElseIf reader.IsStartElement() Then
					Throw New InvalidPlaylistException(reader.Name, PlaylistItem.xmlNode)
				ElseIf Not reader.Read() Then
					Exit Do
				End If
			Loop
			Return Me
		End Function
		''' <summary>
		''' serialize this object
		''' </summary>
		''' <param name="writer">XmlWriter to serialze to</param>
		Friend Sub Serialize(ByVal writer As XmlWriter)
			writer.WriteStartElement(PlaylistItem.xmlNode)
			writer.WriteElementString("Description", Me.Description)
			writer.WriteElementString("FileSize", Me.FileSize.ToString(CultureInfo.InvariantCulture))
			writer.WriteElementString("FrameRate", Me.FrameRate.ToString(CultureInfo.InvariantCulture))
			writer.WriteElementString("Height", Me.VideoHeight.ToString(CultureInfo.InvariantCulture))
			writer.WriteElementString("IsAdaptiveStreaming", Me.IsAdaptiveStreaming.ToString().ToLower(CultureInfo.InvariantCulture))
			writer.WriteElementString("OfflineVideoBitrateInKbps", Me.OfflineVideoBitrateInKbps.ToString(CultureInfo.InvariantCulture))
			writer.WriteElementString("MediaSource", Me.MediaSource.ToString())
			writer.WriteElementString("ThumbSource", Me.ThumbSource.ToString())
			writer.WriteElementString("Title", Me.Title)
			writer.WriteElementString("Width", Me.VideoWidth.ToString(CultureInfo.InvariantCulture))
			If Chapters.Count > 0 Then
				writer.WriteStartElement("Chapters")
				For Each item In Chapters
					item.Serialize(writer)
				Next item
				writer.WriteEndElement()
			End If
			writer.WriteEndElement()
		End Sub
		#End Region
	End Class
End Namespace
